package com.sirui.x.learnIo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sirui.x.learnIo.serializ.SerializUnicode;

public class ServerSocketChannelWrapper {
	
	private static final Logger log = LoggerFactory.getLogger(ServerSocketChannelWrapper.class);

	public static final int LISTEN_PORT = 4010;
	
	private Selector selector;
	
	public void startAndListen() throws IOException {
		ServerSocketChannel ssChannel = ServerSocketChannel.open();
		ssChannel.configureBlocking(false);
		ssChannel.bind(new InetSocketAddress(LISTEN_PORT));
		
		selector = Selector.open();
		ssChannel.register(selector, SelectionKey.OP_ACCEPT);
		
		// 阻塞方法
		while(selector.select() > 0) {
			Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
			while(ite.hasNext()) {
				SelectionKey key = ite.next();
				ite.remove();
				process(key);
			}
			
		}
	}

	private void process(SelectionKey key) {
		try {
			if(key.isAcceptable()) {
				doAcceptable(key);
			}else if(key.isReadable()) {
				doRead(key);
			}else if(key.isWritable()) {
				doWrite(key);
			}else {
				log.info("unkown op: " + key.readyOps());
			}
		} catch (IOException e) {
			log.error("", e);
			try {
				key.channel().close();
			} catch (IOException e1) {
			}
		}
		
	}

	private void doWrite(SelectionKey key) throws IOException {
		SocketChannel sc = (SocketChannel)key.channel();
		String msg = (String)key.attachment();
		byte[] bytes = SerializUnicode.serialization(msg);
		ByteBuffer wrap = ByteBuffer.wrap(bytes);
		sc.write(wrap);
		log.info("write channel: " + msg);
		try {
			Thread.sleep(1000L);
		} catch (InterruptedException e) {
			log.error("", e);
		}
		log.info("socket close.");
		sc.close();
	}

	private void doRead(SelectionKey key) throws IOException {
		SocketChannel sc = (SocketChannel) key.channel();
		ByteBuffer buff = ByteBuffer.allocate(4096);

		int len = sc.read(buff);
		if(len <= 0) {
			log.info("{len:{}}", len);
			sc.close();
			return;
		}
		buff.flip();
		String currMsg = SerializUnicode.deserialization(buff.array(), buff.position(), buff.limit());
		
		String head = key.attachment() == null ? "" : key.attachment().toString();
		String composeMsg = head + currMsg;
		log.info(composeMsg);
		
		if(composeMsg.length() < 20) {
			try {
				Thread.sleep(5000L);
			} catch (InterruptedException e) {
				log.error("", e);
			}
			log.info("data not end. regist read again.");
			sc.register(selector, SelectionKey.OP_READ, composeMsg);
			return;
		}

		// 处理业务逻辑，附上返回结果，异步返回
		log.info("read over and regist write");
		String msg = "HTTP/1.1 200 OK\n\nyes";
		sc.register(selector, SelectionKey.OP_WRITE, msg);
	}

	private void doAcceptable(SelectionKey key) throws IOException {
		ServerSocketChannel sc = (ServerSocketChannel)key.channel();
		SocketChannel accept = sc.accept();
		accept.configureBlocking(false);
		log.info("accept channel. and regist read");
		accept.register(selector, SelectionKey.OP_READ);
	}
}
